建立你自己的完全链上 DAO 来投资 NFT
为您的 NFT 持有者构建 DAO
什么是 DAO?
DAO 代表去中心化自治组织。您可以将 DAO 视为类似于现实世界中的公司。从本质上讲,DAO 允许成员创建治理决策并对其进行投 票。
在传统公司中,当需要做出决定时,公司的董事会或高管负责做出该决定。然而,在 DAO 中,这个过程是民主化的,任何成员都可以创建提案,所有其他成员都可以对其进行投票。创建的每个提案都有一个投票截止日期,在截止日期之后做出有利于投票结果的决定(是或否)。
DAO 的成员资格通常受到 ERC20 代币所有权或 NFT 所有权的限制。成员资格和投票权与您拥有的代币数量成正比的 DAO 示例包括 Uniswap 和 ENS。基于 NFT 的 DAO 示例包括 Meebits DAO。
构建我们的 DAO
你想为你的 CryptoDevs NFT 的持有者启动一个 DAO。从通过 ICO 获得的 ETH 中,你建立了一个 DAO 库。 DAO 现在有很多 ETH,但目前什么也没做。
您希望允许您的 NFT 持有者创建并投票使用该 ETH 从 NFT 市场购买其他 NFT 的提案,并推测价格。也许将来当你卖回 NFT 时,你会将利润分配给 DAO 的所有成员。
要求
- 任何拥有 CryptoDevs NFT 的人都可以创建从 NFT 市场购买不同 NFT 的提案
- 每个拥有 CryptoDevs NFT 的人都可以投票支持或反对活跃的提案
- 每个 NFT 计为每个提案的一票
- 投票者不能对具有相同 NFT 的同一个提案多次投票
- 如果在截止日期前多数 选民投票支持该提案,NFT 购买将自动执行
我们将做什么
- 为了能够在提案通过时自动购买 NFT,您需要一个可以调用 purchase() 函数的链上 NFT 市场。那里有很多 NFT 市场,但为了避免过于复杂,我们将为本教程创建一个简化的假 NFT 市场,因为重点是 DAO。
- 我们还将使用 Hardhat 制作实际的 DAO 智能合约。
- 我们将使用 Next.js 制作网站,以允许用户创建和对提案进行投票
先决条件
- 你已经完成了之前的 NFT Collection 教程。
- 你必须有一些 ETH 给 DAO 财政部
构建
智能合约开发
我们将从创建智能合约开始。我们将制作两个智能合约:
FakeNFTMarketplace.sol
CryptoDevsDAO.sol
为此,我们将使用我们在过去几个教程中一直使用的 Hardhat 开发框架。
为这个项目创建一个名为 DAO-Tutorial 的文件夹,并在该文件夹中打开一个终端窗口。
通过在终端中运行以下命令来设置 新的安全帽项目:
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat
现在您已经安装了 Hardhat,我们可以设置一个项目。在终端中执行以下命令。
在安装 Hardhat 的同一目录中运行:
npx hardhat
确保选择创建 Javascript 项目,然后按照终端中的步骤完成安全帽设置。
现在,让我们从 NPM 安装 @openzeppelin/contracts 包,因为我们将使用 OpenZeppelin 的 Ownable Contract 作为 DAO 合约。
npm install @openzeppelin/contracts
首先,让我们做一个简单的 Fake NFT Marketplace。在 hardhat-tutorial 中的 contracts 目录下创建一个名为 FakeNFTMarketplace.sol 的文件,并添加以下代码。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FakeNFTMarketplace {
/// @dev Maintain a mapping of Fake TokenID to Owner addresses
mapping(uint256 => address) public tokens;
/// @dev Set the purchase price for each Fake NFT
uint256 nftPrice = 0.1 ether;
/// @dev purchase() accepts ETH and marks the owner of the given tokenId as the caller address
/// @param _tokenId - the fake NFT token Id to purchase
function purchase(uint256 _tokenId) external payable {
require(msg.value == nftPrice, "This NFT costs 0.1 ether");
tokens[_tokenId] = msg.sender;
}
/// @dev getPrice() returns the price of one NFT
function getPrice() external view returns (uint256) {
return nftPrice;
}
/// @dev available() checks whether the given tokenId has already been sold or not
/// @param _tokenId - the tokenId to check for
function available(uint256 _tokenId) external view returns (bool) {
// address(0) = 0x0000000000000000000000000000000000000000
// This is the default value for addresses in Solidity
if (tokens[_tokenId] == address(0)) {
return true;
}
return false;
}
}
FakeNFTMarketplace 公开了一些基本功能,如果提案获得通过,我们将从 DAO 合约中使用这些功能来购买 NFT。 真正的 NFT 市场会更加复杂——因为并非所有 NFT 的价格都相同。
在开始编写 DAO 合约之前,让我们确保一切都编译好。 在终端的 hardhat-tutorial 文件夹中运行以下命令。
npx hardhat compile
并确保没有编译错误。
现在,我们将开始编写 CryptoDevsDAO 合约。 由于这主要是一个完全自定义的合约,并且比我们目前所做的相对复杂,所以我们将一点一点地解释这一点。 首先,让我们为合约编写样板代码。 在 hardhat-tutorial 的 contracts 目录下创建一个名为 CryptoDevsDAO.sol 的新文件,并将以下代码添加到其中。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
// We will add the Interfaces here
contract CryptoDevsDAO is Ownable {
// We will write contract code here
}
现在,我们需要调用 FakeNFTMarketplace 合约以及您之前部署的 CryptoDevs NFT 合约的函 数。 回想一下高级 Solidity 主题教程,我们需要为这些合约提供一个接口,因此该合约知道哪些函数可以调用,它们将什么作为参数以及它们返回什么。
通过添加以下代码将以下两个接口添加到您的代码中
/**
* Interface for the FakeNFTMarketplace
*/
interface IFakeNFTMarketplace {
/// @dev getPrice() returns the price of an NFT from the FakeNFTMarketplace
/// @return Returns the price in Wei for an NFT
function getPrice() external view returns (uint256);
/// @dev available() returns whether or not the given _tokenId has already been purchased
/// @return Returns a boolean value - true if available, false if not
function available(uint256 _tokenId) external view returns (bool);
/// @dev purchase() purchases an NFT from the FakeNFTMarketplace
/// @param _tokenId - the fake NFT tokenID to purchase
function purchase(uint256 _tokenId) external payable;
}
/**
* Minimal interface for CryptoDevsNFT containing only two functions
* that we are interested in
*/
interface ICryptoDevsNFT {
/// @dev balanceOf returns the number of NFTs owned by the given address
/// @param owner - address to fetch number of NFTs for
/// @return Returns the number of NFTs owned
function balanceOf(address owner) external view returns (uint256);
/// @dev tokenOfOwnerByIndex returns a tokenID at given index for owner
/// @param owner - address to fetch the NFT TokenID for
/// @param index - index of NFT in owned tokens array to fetch
/// @return Returns the TokenID of the NFT
function tokenOfOwnerByIndex(address owner, uint256 index)
external
view
returns (uint256);
}
现在,让我们考虑一下我们在 DAO 合约中需要哪些功能。